#include "include/matdemo.h"

#include <iostream>
#include <filesystem>
#include <opencv2/dnn.hpp>

using namespace std;
using namespace cv::dnn;

void MatDemo::clone_test(cv::Mat &img)
{
    // cv::Mat img1,img2;

    // 两种拷贝方式
    //  img1 = img.clone();
    //  img.copyTo(img2);

    // namedWindow("img", WINDOW_AUTOSIZE);
    // imshow("img", img);
    // waitKey(0);

    cv::Mat img3 = cv::Mat::zeros(cv::Size(8, 8), CV_8UC1);
    cout << "width:" << img3.cols << ",height:" << img3.rows << ",channels:" << img3.channels() << endl;
    cout << img3 << endl;

    cv::Mat img4 = cv::Mat::ones(cv::Size(8, 8), CV_8UC1);
    cout << "width:" << img4.cols << ",height:" << img4.rows << ",channels:" << img4.channels() << endl;
    cout << img4 << endl;

    cv::Mat img5 = cv::Mat::eye(cv::Size(8, 8), CV_8UC1); // 单位矩阵
    cout << "width:" << img5.cols << ",height:" << img5.rows << ",channels:" << img5.channels() << endl;
    cout << img5 << endl;

    // cv::Mat img6 = cv::Mat::diag(cv::Mat(cv::Size(8,8),CV_8UC1,cv::Scalar(1)));

    cv::Mat img6 = cv::Mat::zeros(cv::Size(256, 256), CV_8UC3);
    img6.setTo(cv::Scalar(255, 0, 0)); // 纯蓝底色图片
    namedWindow("img6", cv::WINDOW_AUTOSIZE);
    imshow("img6", img6);
    waitKey(0);

    cv::destroyAllWindows();
}

/**
 * 像素访问的两种基本方法
 */
void MatDemo::pixel_visit_test(cv::Mat &img)
{
    int w = img.cols;
    int h = img.rows;
    int channel = img.channels();
    int type = img.type();

    cv::Mat dstImg = cv::Mat::zeros(cv::Size(w, h), CV_8UC3);

    // 通过下标访问
    for (int i = 0; i < h; i++)
    {
        for (int j = 0; j < w; j++)
        {
            if (type == CV_8UC3)
            {
                // 三通道处理
                // saturate_cast用于限定数据范围
                dstImg.at<cv::Vec3b>(i, j)[0] = saturate_cast<uchar>(img.at<cv::Vec3b>(i, j)[0] * 0.5);
                dstImg.at<cv::Vec3b>(i, j)[1] = saturate_cast<uchar>(img.at<cv::Vec3b>(i, j)[1] * 0.5);
                dstImg.at<cv::Vec3b>(i, j)[2] = saturate_cast<uchar>(img.at<cv::Vec3b>(i, j)[2] * 0.5);
            }
            else if (type == CV_8UC1)
            {
                // 单通道处理
                dstImg.at<cv::Vec3b>(i, j)[0] = img.at<uchar>(i, j);
            }
        }
    }

    cv::Mat dstImg1 = cv::Mat::zeros(img.rows, img.cols, CV_8UC3);

    // 通过指针访问
    for (int i = 0; i < img.rows; i++)
    {
        uchar *p = img.ptr<uchar>(i);
        uchar *tp = dstImg1.ptr<uchar>(i);
        for (int j = 0; j < img.cols; j++)
        {
            if (type == CV_8UC3)
            {
                *tp++ = saturate_cast<uchar>(*p++ + 50);
                *tp++ = saturate_cast<uchar>(*p++ + 50);
                *tp++ = saturate_cast<uchar>(*p++ + 50);
            }
            else if (type == CV_8UC1)
            {
                *tp++ = saturate_cast<uchar>(*p++ + 50);
            }
        }
    }

    namedWindow("src", WINDOW_AUTOSIZE);
    imshow("src", img);

    namedWindow("dst", WINDOW_AUTOSIZE);
    imshow("dst", dstImg);

    namedWindow("dst2", WINDOW_AUTOSIZE);
    imshow("dst2", dstImg1);

    waitKey(0);
    destroyAllWindows();
}

// 项目运算,mat几乎重载的所有运算符
void MatDemo::pixel_operators_test(Mat &img)
{

    namedWindow("src", WINDOW_AUTOSIZE);
    imshow("src", img);

    Mat dstImg = Mat::zeros(img.size(), img.type());
    dstImg.setTo(Scalar(50, 50, 50));
    dstImg += img; // 直接加法运算 当然也有封装的add和subtract方法
    // add(img, dstImg, dstImg);
    // subtract(img, dstImg, dstImg);

    namedWindow("dst", WINDOW_AUTOSIZE);
    imshow("dst", dstImg);

    Mat dstImg1 = Mat::zeros(img.size(), img.type());
    dstImg1.setTo(Scalar(2, 2, 2));
    // dstImg1 *= img; //直接乘报错
    multiply(dstImg1, img, dstImg1); // 需要使用这个方法

    namedWindow("dst1", WINDOW_AUTOSIZE);
    imshow("dst1", dstImg1);

    Mat dstImg2 = Mat::zeros(img.size(), img.type());
    dstImg2.setTo(Scalar(2, 2, 2));
    divide(img, dstImg2, dstImg2);

    namedWindow("dst2", WINDOW_AUTOSIZE);
    imshow("dst2", dstImg2);

    waitKey(0);
    destroyAllWindows();
}

Mat src, m, dst;
// int current_value = 50;
// trackbar的回调函数
static void on_track(int d, void *)
{
    m = Scalar(d, d, d);
    add(src, m, dst);
    cout << "bright:" << d << endl;
    imshow("src", dst);
}

void MatDemo::tracking_bar_test(Mat &img)
{
    src = img;
    m = Mat::zeros(src.size(), src.type());
    int max_value = 100;
    int min_value = 0;

    // createTrackbar("bright", "lighting", &current_value, max_value, [](int, void*){

    // });
    namedWindow("src", WINDOW_AUTOSIZE);
    createTrackbar("bright", "src", 0, max_value, on_track);
    on_track(0, 0); // 调用下这个方法生成dst

    imshow("src", dst);
    waitKey(0);
    destroyAllWindows();
}

void MatDemo::tracking_bar_test2(Mat &img)
{
    namedWindow("win1", WINDOW_AUTOSIZE);
    int bright = 50;
    createTrackbar("bright", "win1", &bright, 100, [](int num, void *data)
                   {
        Mat &src = *(Mat*)data;
        Mat m = Mat::zeros(src.size(),src.type());
        m = Scalar(num,num,num);
        Mat dst;
        add(src,m,dst);
        imshow("win1",dst); }, (void *)&img);

    int contrast = 100;
    createTrackbar("contrast", "win1", &contrast, 200, [](int num, void *data)
                   {
        Mat &src = *(Mat*)data;
        Mat m = Mat::zeros(src.size(),src.type());
        // m = Scalar(num,num,num);
        Mat dst;
        // addWeighted(src,1.0,m,0,num,dst); //调整亮度
        double contrast_factor = num/100.0;
        addWeighted(src,contrast_factor,m,0,0,dst); //对比度调整
        imshow("win1",dst); }, (void *)&img);
    imshow("win1", img);
    waitKey(0);
    destroyAllWindows();
}

// 键盘输入demo
void MatDemo::keyboard_test(cv::Mat &img)
{
    namedWindow("win1", WINDOW_AUTOSIZE);
    imshow("win1", img);

    Mat dst = Mat::zeros(img.size(), img.type());
    imshow("win1", img);
    while (true)
    {
        int c = waitKey(1); // 获取键盘键入的字符
        if (c == 27)
        {
            break; // esc退出
        }
        switch (c)
        {
        case 49:
            // 显示灰度图像
            cvtColor(img, dst, COLOR_BGR2GRAY);
            imshow("win1", dst);

            cout << "show gray image." << endl;
            break;
        case 50:
            // 显示hsv颜色空间
            cvtColor(img, dst, COLOR_BGR2HSV);
            imshow("win1", dst);

            cout << "show hsv image." << endl;
            break;
        case 51:
            dst = Scalar(50, 50, 50);
            cv::add(img, dst, dst);
            imshow("win1", dst);

            cout << "add bright +50" << endl;
            break;
        default:
            break;
        }
    }

    destroyAllWindows();
}

// 图片风格demo
void MatDemo::color_style_test(cv::Mat &img)
{
    int types[] = {
        COLORMAP_AUTUMN,           //!< ![autumn](pics/colormaps/colorscale_autumn.jpg)
        COLORMAP_BONE,             //!< ![bone](pics/colormaps/colorscale_bone.jpg)
        COLORMAP_JET,              //!< ![jet](pics/colormaps/colorscale_jet.jpg)
        COLORMAP_WINTER,           //!< ![winter](pics/colormaps/colorscale_winter.jpg)
        COLORMAP_RAINBOW,          //!< ![rainbow](pics/colormaps/colorscale_rainbow.jpg)
        COLORMAP_OCEAN,            //!< ![ocean](pics/colormaps/colorscale_ocean.jpg)
        COLORMAP_SUMMER,           //!< ![summer](pics/colormaps/colorscale_summer.jpg)
        COLORMAP_SPRING,           //!< ![spring](pics/colormaps/colorscale_spring.jpg)
        COLORMAP_COOL,             //!< ![cool](pics/colormaps/colorscale_cool.jpg)
        COLORMAP_HSV,              //!< ![HSV](pics/colormaps/colorscale_hsv.jpg)
        COLORMAP_PINK,             //!< ![pink](pics/colormaps/colorscale_pink.jpg)
        COLORMAP_HOT,              //!< ![hot](pics/colormaps/colorscale_hot.jpg)
        COLORMAP_PARULA,           //!< ![parula](pics/colormaps/colorscale_parula.jpg)
        COLORMAP_MAGMA,            //!< ![magma](pics/colormaps/colorscale_magma.jpg)
        COLORMAP_INFERNO,          //!< ![inferno](pics/colormaps/colorscale_inferno.jpg)
        COLORMAP_PLASMA,           //!< ![plasma](pics/colormaps/colorscale_plasma.jpg)
        COLORMAP_VIRIDIS,          //!< ![viridis](pics/colormaps/colorscale_viridis.jpg)
        COLORMAP_CIVIDIS,          //!< ![cividis](pics/colormaps/colorscale_cividis.jpg)
        COLORMAP_TWILIGHT,         //!< ![twilight](pics/colormaps/colorscale_twilight.jpg)
        COLORMAP_TWILIGHT_SHIFTED, //!< ![twilight shifted](pics/colormaps/colorscale_twilight_shifted.jpg)
        COLORMAP_TURBO,            //!< ![turbo](pics/colormaps/colorscale_turbo.jpg)
        COLORMAP_DEEPGREEN         //!< ![deepgreen](pics/colormaps/colorscale_deepgreen.jpg)
    };
    int current_index = 0;
    Mat dst = img.clone();
    namedWindow("win1", WINDOW_AUTOSIZE);
    imshow("win1", dst);
    while (true)
    {
        int c = waitKey(1000);
        cout << "key: " << c << endl;
        if (c == 27)
        {
            break;
        }
        if (c == 49)
        {
            imwrite("../res/color_map.jpg", dst);
        }
        if (c == -1)
        {
            applyColorMap(img, dst, types[current_index++ % 22]);
            imshow("win1", dst);
        }
    }

    destroyAllWindows();
}

// 像素逻辑操作
void MatDemo::bitwise_test(Mat &img)
{
    Mat m1 = Mat::zeros(Size(256, 256), CV_8UC3);
    Mat m2 = Mat::zeros(Size(256, 256), CV_8UC3);
    // 绘制矩形
    rectangle(m1, Rect(100, 100, 80, 80), Scalar(0, 0, 255), -1, LINE_8, 0);
    rectangle(m2, Rect(150, 150, 80, 80), Scalar(0, 255, 255), -1, LINE_8, 0);

    namedWindow("m1", WINDOW_AUTOSIZE);
    imshow("m1", m1);

    namedWindow("m2", WINDOW_AUTOSIZE);
    imshow("m2", m2);

    Mat dst;
    // 逻辑与
    //  bitwise_and(m1, m2, dst);
    // 逻辑或
    bitwise_or(m1, m2, dst);
    // 逻辑异或
    //  bitwise_xor(m1, m2, dst);
    // 逻辑非
    //  bitwise_not(m1, dst);
    namedWindow("dst", WINDOW_AUTOSIZE);
    imshow("dst", dst);

    waitKey(0);
    destroyAllWindows();
}

/**
 * 通道demo。
 * 通道分离
 * 通道合并
 */
void MatDemo::channel_test(cv::Mat &img)
{
    vector<cv::Mat> channels;
    cv::split(img, channels); // 分离通道

    // 显示分离通道图像
    for (int i = 0; i < channels.size(); i++)
    {
        cv::imshow(cv::format("channel %d", i), channels[i]);
    }

    // 合并通道，即显示原图
    // 这里合并时可以打乱数组顺序，这样获得的图片就和原图不同了，
    cv::Mat dst;
    cv::merge(channels, dst);
    cv::imshow("merge", dst);

    cv::Mat mixedDst = Mat::zeros(img.size(), img.type());
    int from_to[] = {0, 2, 1, 1, 2, 0};                 // 这里表示把0通道和2通道交换
    cv::mixChannels(&img, 1, &mixedDst, 1, from_to, 3); // 1 1 3表示一个图源 一个结果图 3个映射对
    cv::imshow("mixChannels", mixedDst);

    waitKey(0);
    destroyAllWindows();
}

/**
 * 替换图片背景色的示例
 */
void MatDemo::inrange_test(Mat &img)
{
    Mat hsv;
    cvtColor(img, hsv, COLOR_BGR2HSV);
    // imshow("hsv", hsv);
    Mat mask;
    // 过滤绿色波段，把在范围内的像素变成白色，不在范围内的编程黑色
    inRange(hsv, Scalar(35, 43, 46), Scalar(77, 255, 255), mask);
    imshow("mask", mask);

    Mat redback = Mat::zeros(img.size(), img.type());
    redback = Scalar(40, 40, 200); // 纯红色背景
    bitwise_not(mask, mask);       // 这里将mask取反
    imshow("mask1", mask);
    img.copyTo(redback, mask); // 这个拷贝的效果就是将img中在mask范围内的像素拷贝到redback中
    // imshow("redback", redback);

    waitKey(0);
    destroyAllWindows();
}

/**
 * 像素统计测试
 */
void MatDemo::pixel_statistics_test(cv::Mat &img)
{
    double min, max;
    Point minloc, maxloc;
    // 获取最小值最大值及其位置
    vector<Mat> channels;
    cv::split(img, channels);
    for (int i = 0; i < channels.size(); i++)
    {
        minMaxLoc(channels[i], &min, &max, &minloc, &maxloc, Mat());
        cout << "min:" << min << " max:" << max << endl;
    }
    Mat mean, stddev;

    meanStdDev(img, mean, stddev); // 均值和方差
    cout << "mean:" << mean.at<double>(0, 0) << "," << mean.at<double>(0, 1) << "," << mean.at<double>(0, 2) << " stddev:" << stddev << endl;
}

/**
 * 基本图形绘制演示
 */
void MatDemo::draw_graphic_test(cv::Mat &img)
{
    cv::rectangle(img, cv::Point(100, 100), cv::Point(200, 200), cv::Scalar(0, 0, 255), 2);
    cv::circle(img, cv::Point(250, 250), 50, cv::Scalar(255, 255, 0), -1);
    cv::line(img, cv::Point(300, 300), cv::Point(400, 400), cv::Scalar(0, 255, 0), 2);
    cv::putText(img, "Hello OpenCV", cv::Point(100, 100), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(0, 0, 255), 2);

    namedWindow("draw_graphic_test", cv::WINDOW_AUTOSIZE);
    imshow("draw_graphic_test", img);
    waitKey(0);
    destroyWindow("draw_graphic_test");
}

void MatDemo::draw_graphic_test1(cv::Mat &img)
{
    Mat bg = Mat::zeros(img.size(), img.type());
    cv::rectangle(bg, cv::Point(250, 100), cv::Point(500, 300), cv::Scalar(0, 255, 255), -2);
    Mat dst;
    addWeighted(img, 1, bg, 0.3, 0, dst);

    namedWindow("draw_graphic_test1", cv::WINDOW_AUTOSIZE);
    imshow("draw_graphic_test1", dst);
    waitKey(0);
    destroyWindow("draw_graphic_test1");
}

/**
 * 随机绘制
 */
void MatDemo::random_draw_test(cv::Mat &img)
{
    cv::RNG rng;
    Mat bg = Mat::zeros(img.size(), img.type());
    cv::randn(bg, cv::Scalar::all(250), cv::Scalar::all(255)); // 随机生成背景
    addWeighted(img, 1, bg, 0.3, 0, img);
    namedWindow("random_draw_test", cv::WINDOW_AUTOSIZE);
    imshow("random_draw_test", img);
    while (true)
    {
        int c = waitKey(1);
        if (c == 27)
        {
            break;
        }
        int x1 = rng.uniform(0, img.cols); // 生成随机数
        int y1 = rng.uniform(0, img.rows);
        int x2 = rng.uniform(0, img.cols);
        int y2 = rng.uniform(0, img.rows);
        int b = rng.uniform(0, 255);
        int g = rng.uniform(0, 255);
        int r = rng.uniform(0, 255);
        line(img, cv::Point(x1, y1), cv::Point(x2, y2), cv::Scalar(b, g, r), 1); // 绘制随机线
        imshow("random_draw_test", img);
        // polylines(img, points, true, cv::Scalar(b, g, r), 1);绘制多边形
        // fillPoly(img, points, cv::Scalar(b, g, r));填充多边形
        // drawContours(img, points, -1, cv::Scalar(b, g, r), 1); //填充加绘制
    }
    destroyWindow("random_draw_test");
}

int startx = -1, starty = -1;
static void onMouse(int event, int x, int y, int flags, void *param)
{
    // cout<<"x:"<<x<<" y:"<<y<<"falgs:"<<flags<<" event:"<<event<<endl;
    if (flags == cv::EVENT_MBUTTONDBLCLK)
    {
        cv::Mat *img = (cv::Mat *)param;
        if (startx < 0 || starty < 0)
        {
            startx = x;
            starty = y;
            // cout << "startx:" << startx << " starty:" << starty << endl;
        }
        else
        {
            int radius = static_cast<int>(sqrt(pow(x - startx, 2) + pow(y - starty, 2)) / 2);
            Mat dst = Mat::zeros(img->size(), img->type());
            cv::circle(dst, cv::Point(abs(x + startx) / 2, abs(y + starty) / 2), radius, cv::Scalar(0, 0, 255), -1);
            addWeighted(*img, 1, dst, 1, 0, dst);
            imshow("mouse_event_test", dst);
            // startx=-1;
            // starty=-1;
        }
    }
    // cv::Mat *img = (cv::Mat *)param;
    // flag:0 move,1 left,2:right,4:ctrl,8:shift,16:alt等等，具体可以测试
    // cout<<"x:"<<x<<" y:"<<y<<"falgs:"<<flags<<endl;
}

/**
 * 鼠标事件处理
 */
void MatDemo::mouse_event_test(cv::Mat &img)
{
    namedWindow("mouse_event_test", cv::WINDOW_AUTOSIZE);
    setMouseCallback("mouse_event_test", onMouse, &img);

    imshow("mouse_event_test", img);
    while (true)
    {
        int c = waitKey(1);
        if (c == 27)
        {
            break;
        }
    }
}

static void onMouse1(int event, int x, int y, int flags, void *param)
{
    if (flags == cv::EVENT_MBUTTONDBLCLK)
    {
        cv::Mat *img = (cv::Mat *)param;
        if (startx < 0 || starty < 0)
        {
            startx = x;
            starty = y;
        }
        else
        {
            Rect rio = Rect(startx, starty, abs(x - startx) % img->cols, abs(y - starty) % img->rows);
            // namedWindow("mouse_event_test2", cv::WINDOW_NORMAL);
            imshow("mouse_event_test2", (*img)(rio));
            // startx=-1;
            // starty=-1;
        }
    }
}

/**
 * 鼠标截图示例
 */
void MatDemo::cut_img_test(cv::Mat &img)
{
    namedWindow("cut_img_test", cv::WINDOW_AUTOSIZE);
    setMouseCallback("cut_img_test", onMouse1, &img);
    imshow("cut_img_test", img);
    while (true)
    {
        int c = waitKey(1);
        if (c == 27)
        {
            break;
        }
    }
}

/**
 * 归一化测试
 * 把像素数据转成浮点数后必须归一化才能正确展示图片
 */
void MatDemo::normalize_test(cv::Mat &img)
{
    Mat dst;
    img.convertTo(dst, CV_32F); // 转换成32位浮点数，此时没有归一化是无法展示图片的
    cout << dst.type() << endl;
    cout << img.type() << endl;

    normalize(dst, dst, 0, 1, NORM_MINMAX); // 归一化
    // cout<<dst<<endl;

    // 归一化后还是可以显示图片的
    namedWindow("normalize_test", cv::WINDOW_AUTOSIZE);
    imshow("normalize_test", dst);
    waitKey(0);

    destroyAllWindows();
}

/**
 * 缩放示例,
 * 缩放的本质是插值算法的使用
 */
void MatDemo::resize_test(Mat &img)
{
    Mat zoomin, zoomout;
    int h = img.rows;
    int w = img.cols;
    resize(img, zoomin, Size(w / 2, h / 2), 0, 0, INTER_LINEAR);
    resize(img, zoomout, Size(w * 2, h * 2), 0, 0, INTER_LINEAR);

    namedWindow("zoomin", cv::WINDOW_AUTOSIZE);
    imshow("zoomin", zoomin);

    namedWindow("zoomout", cv::WINDOW_AUTOSIZE);
    imshow("zoomout", zoomout);

    waitKey(0);

    destroyAllWindows();
}

/**
 * 翻转示例
 */
void MatDemo::flip_test(Mat &img)
{
    Mat flip_img;
    flip(img, flip_img, -1); // 后一个参数表示翻转的方向 0 1 -1
    namedWindow("flip_img", cv::WINDOW_AUTOSIZE);
    imshow("flip_img", flip_img);
    waitKey(0);
    destroyAllWindows();
}

/**
 * 旋转示例
 */
void MatDemo::rotate_test(Mat &img)
{
    Mat rotate_img;
    resize(img, rotate_img, Size(img.cols / 2, img.rows / 2), 0, 0, INTER_LINEAR);
    rotate(rotate_img, rotate_img, ROTATE_90_CLOCKWISE); // 后一个参数表示旋转的角度
    namedWindow("rotate_img", cv::WINDOW_AUTOSIZE);
    imshow("rotate_img", rotate_img);
    waitKey(0);
    destroyAllWindows();
}

/**
 * 旋转
 */
void MatDemo::rotate_test1(Mat &img)
{
    Mat dst, M;
    int w = img.cols;
    int h = img.rows;
    resize(img, dst, Size(w / 2, h / 2), 0, 0, INTER_LINEAR);

    M = getRotationMatrix2D(Point2f(w / 2, h / 2), 30, 1);                   // 源图片的中心点,旋转的角度,缩放比例
    warpAffine(dst, dst, M, dst.size(), INTER_LINEAR, 0, Scalar(0, 255, 0)); // 旋转矩阵应用于图片

    namedWindow("rotate_img", cv::WINDOW_AUTOSIZE);
    imshow("rotate_img", dst);
    waitKey(0);

    destroyAllWindows();
}

/**
 * 摄像头调用
 */
void MatDemo::video_test()
{
    VideoCapture cvCapture(0); // 这里0表示默认摄像头编号，如果要打开视频文件这里填写视频文件路径
    int frame_width = cvCapture.get(CAP_PROP_FRAME_WIDTH);
    int frame_height = cvCapture.get(CAP_PROP_FRAME_HEIGHT);
    int count = cvCapture.get(CAP_PROP_FRAME_COUNT);
    double fps = cvCapture.get(CAP_PROP_FPS);
    // cout << cv::getBuildInformation()<< endl; // 打印opencv信息
    cout << "frame_width:" << frame_width << endl;
    cout << "frame_height:" << frame_height << endl;
    cout << "count:" << count << endl;
    cout << "fps:" << fps << endl;

    // 保存视频 VideoWriter::fourcc('M','J','P','G')
    // VideoWriter::fourcc('H','2','6','4') 这个需要ffmpeg支持，opencv编译时需要打开这个选项
    // 如果视频源是视频文件，这里第二个参数为 cvCapture.get(CAP_PROP_FOCUS)
    VideoWriter videoWriter("../res/test.mp4", VideoWriter::fourcc('M', 'J', 'P', 'G'), -1, Size(frame_width, frame_height));

    cout << "Available codecs:" << std::endl;
    for (int i = 0; i < 100; ++i)
    {
        char codec[5];
        codec[0] = (char)(i >> 24);
        codec[1] = (char)(i >> 16);
        codec[2] = (char)(i >> 8);
        codec[3] = (char)i;
        codec[4] = '\0';
        VideoWriter testWriter("../res/test.avi", i, 30, Size(640, 480), true);
        if (testWriter.isOpened())
        {
            std::cout << "Codec: " << codec << " (" << i << ")" << std::endl;
            testWriter.release();
        }
    }

    if (!videoWriter.isOpened())
    {
        cout << "Could not open the output video for write" << endl;
        return;
    }

    Mat frame;
    while (true)
    {
        cvCapture.read(frame);
        if (frame.empty())
        {
            break;
        }
        // flip(frame, frame, 1); // 左右翻转
        Mat dg = Mat::zeros(frame.size(), frame.type());
        // dg = Scalar(0, 255, 0);
        // addWeighted(frame, 0.7, dg, 0.3, 0, frame);
        videoWriter.write(frame); // 保存帧到视频
        imshow("video", frame);
        int c = waitKey(10);
        if (c == 27)
        {
            break;
        }
    }

    destroyAllWindows();
    cvCapture.release(); // 记得释放!
    videoWriter.release();
}

/**
 * 直方图示例
 */
void MatDemo::calcHist_test(Mat &img)
{
    Mat gray;
    cvtColor(img, gray, COLOR_BGR2GRAY); // 灰度化
    namedWindow("gray", WINDOW_AUTOSIZE);
    imshow("gray", gray);

    const int channels[] = {0};
    Mat hist;
    int dims = 1;
    const int histSize[] = {256};
    float range[] = {0, 255};
    const float *ranges[] = {range};
    calcHist(&gray, 1, channels, Mat(), hist, dims, histSize, ranges, true, false); // 计算直方图

    // 绘制直方图
    int scale = 2;
    int hist_height = 256;
    Mat hist_img = Mat::zeros(hist_height, 256 * scale, CV_8UC3);
    double max_val;
    minMaxLoc(hist, 0, &max_val, 0, 0); // 计算直方图的最大像素值

    for (int i = 0; i < 256; i++)
    {
        float bin_val = hist.at<float>(i);
        int intensity = cvRound(bin_val * hist_height / max_val);
        rectangle(hist_img, Point(i * scale, hist_height), Point((i + 1) * scale, hist_height - intensity), Scalar(255, 255, 255), -1);
    }

    imshow("hist", hist_img);

    waitKey(0);
    destroyAllWindows();
}

void MatDemo::calcHist_test1(cv::Mat &img)
{
    Mat dst;
    cvtColor(img, dst, COLOR_BGR2HSV); // hsv颜色空间
    vector<Mat> channels;
    split(dst, channels); // 分离通道, 得到3个通道的数组 (H, S, V)

    // 定义直方图参数
    int histSize_h = 180;        // H 通道的直方图大小
    int histSize_sv = 256;       // S 和 V 通道的直方图大小
    float range_h[] = {0, 180};  // H 通道的范围
    float range_sv[] = {0, 256}; // S 和 V 通道的范围
    const float *histRange_h = {range_h};
    const float *histRange_sv = {range_sv};

    // 计算每个通道的直方图
    Mat hist_h, hist_s, hist_v;
    calcHist(&channels[0], 1, 0, Mat(), hist_h, 1, &histSize_h, &histRange_h);
    calcHist(&channels[1], 1, 0, Mat(), hist_s, 1, &histSize_sv, &histRange_sv);
    calcHist(&channels[2], 1, 0, Mat(), hist_v, 1, &histSize_sv, &histRange_sv);

    // 规范化直方图
    normalize(hist_h, hist_h, 0, 400, NORM_MINMAX);
    normalize(hist_s, hist_s, 0, 400, NORM_MINMAX);
    normalize(hist_v, hist_v, 0, 400, NORM_MINMAX);

    // 创建图像以绘制直方图
    int hist_width = 512;  // 直方图图像宽度
    int hist_height = 400; // 直方图图像高度
    int bin_width_h = cvRound((double)hist_width / histSize_h);
    int bin_width_sv = cvRound((double)hist_width / histSize_sv);

    Mat histImage_h(hist_height, hist_width, CV_8UC3, Scalar(0, 0, 0));
    Mat histImage_s(hist_height, hist_width, CV_8UC3, Scalar(0, 0, 0));
    Mat histImage_v(hist_height, hist_width, CV_8UC3, Scalar(0, 0, 0));

    // 绘制直方图
    for (int i = 1; i < histSize_h; i++)
    {
        line(histImage_h, Point(bin_width_h * (i - 1), hist_height - cvRound(hist_h.at<float>(i - 1))),
             Point(bin_width_h * (i), hist_height - cvRound(hist_h.at<float>(i))),
             Scalar(255, 0, 0), 2, 8, 0);
    }

    for (int i = 1; i < histSize_sv; i++)
    {
        line(histImage_s, Point(bin_width_sv * (i - 1), hist_height - cvRound(hist_s.at<float>(i - 1))),
             Point(bin_width_sv * (i), hist_height - cvRound(hist_s.at<float>(i))),
             Scalar(0, 255, 0), 2, 8, 0);
    }

    for (int i = 1; i < histSize_sv; i++)
    {
        line(histImage_v, Point(bin_width_sv * (i - 1), hist_height - cvRound(hist_v.at<float>(i - 1))),
             Point(bin_width_sv * (i), hist_height - cvRound(hist_v.at<float>(i))),
             Scalar(0, 0, 255), 2, 8, 0);
    }

    // 显示直方图图像
    // namedWindow("Histogram H", WINDOW_AUTOSIZE);
    // imshow("Histogram H", histImage_h);

    // namedWindow("Histogram S", WINDOW_AUTOSIZE);
    // imshow("Histogram S", histImage_s);

    // namedWindow("Histogram V", WINDOW_AUTOSIZE);
    // imshow("Histogram V", histImage_v);
    Mat result;
    addWeighted(histImage_h, 1, histImage_s, 1, 0, result);
    addWeighted(result, 1, histImage_v, 1, 0, result);
    namedWindow("Histogram", WINDOW_AUTOSIZE);
    imshow("Histogram", result);

    waitKey(0);
    destroyAllWindows();
}

/**
 * 直方图均衡化
 */
void MatDemo::histgram_test(cv::Mat &img)
{
    cv::Mat img1 = cv::imread("../res/smoothed_image.jpg");
    Mat gray;
    cvtColor(img1, gray, COLOR_BGR2GRAY);
    Mat dst;
    // 均衡化
    equalizeHist(gray, dst);

    imshow("gray", gray);

    // applyColorMap(dst, dst, COLORMAP_JET);
    Mat dst1;
    bilateralFilter(dst, dst1, 0, 100, 10);
    imshow("dst", dst1);
    waitKey(0);

    destroyAllWindows();
}

/**
 * 卷积
 */
void MatDemo::blur_test(cv::Mat &img)
{
    Mat dst;
    blur(img, dst, Size(5, 5), Point(-1, -1)); // 均值卷积会变模糊,最后一个参数表示卷积结果替换哪个位置的值，默认为卷积核中间

    namedWindow("blur", WINDOW_AUTOSIZE);
    imshow("blur", dst);
    waitKey(0);
    destroyAllWindows();
}

/**
 * 高斯模糊
 */
void MatDemo::gaussian_test(cv::Mat &img)
{
    Mat dst;
    GaussianBlur(img, dst, Size(5, 5), 15);

    namedWindow("gaussian_blur", WINDOW_AUTOSIZE);
    imshow("gaussian_blur", dst);
    waitKey(0);
    destroyAllWindows();
}

/**
 * 高斯双边滤波
 */
void MatDemo::bilateral_test(cv::Mat &img)
{
    Mat dst;
    bilateralFilter(img, dst, 0, 100, 10);

    namedWindow("bilateral_filter", WINDOW_AUTOSIZE);
    imshow("bilateral_filter", dst);
    waitKey(0);
    destroyAllWindows();
}

/**
 * 人脸检测测试
 */
void MatDemo::face_detect_test()
{
    // 加载模型文件
    std::string pb_file_path = "../models/face/opencv_face_detector_uint8.pb";
    std::string pbtxt_file_path = "../models/face/opencv_face_detector.pbtxt";

    cv::dnn::Net net = cv::dnn::readNetFromTensorflow(pb_file_path, pbtxt_file_path);
    VideoCapture cap(0);
    cv::Mat frame;
    while (true)
    {
        cap >> frame;
        cv::Mat blob = cv::dnn::blobFromImage(frame, 1.0, cv::Size(300, 300), cv::Scalar(104, 117, 123), false, false);
        net.setInput(blob);
        cv::Mat probs = net.forward(); // 推理
        cv::Mat detectMat(probs.size[2], probs.size[3], CV_32F, probs.ptr<float>());
        for (int i = 0; i < detectMat.rows; i++)
        {
            float confidence = detectMat.at<float>(i, 2);
            if (confidence > 0.5)
            {
                int x1 = static_cast<int>(detectMat.at<float>(i, 3) * frame.cols);
                int y1 = static_cast<int>(detectMat.at<float>(i, 4) * frame.rows);
                int x2 = static_cast<int>(detectMat.at<float>(i, 5) * frame.cols);
                int y2 = static_cast<int>(detectMat.at<float>(i, 6) * frame.rows);
                cv::Rect rect(x1, y1, x2 - x1, y2 - y1);
                cv::rectangle(frame, rect, cv::Scalar(0, 255, 0), 2, 8);
            }
        }

        // cap.read(frame);
        cv::imshow("frame", frame);
        int c = waitKey(1);
        if (c == 27)
        {
            break;
        }
    }

    cap.release();
    destroyAllWindows();
}

void MatDemo::show_local_video()
{
    cv::VideoCapture cap("../res/output_video.mp4");
    if (!cap.isOpened())
    {
        std::cerr << "Error: Could not open video file." << std::endl;
        return;
    }
    cv::Mat frame;
    while (true)
    {
        cap >> frame;
        cv::imshow("frame", frame);
        int c = waitKey(1);
        if (c == 27)
        {
            break;
        }
    }
    cap.release();
    destroyAllWindows();
}

void MatDemo::save_video()
{
    int frame_width = 640;
    int frame_height = 480;
    double fps = 30;

    std::filesystem::path outputPath("D:/projects/cpp-opencv_cmake/res/test.avi");
    std::filesystem::path dir = outputPath.parent_path();

    if (!std::filesystem::exists(dir))
    {
        std::filesystem::create_directories(dir);
    }

    string outputFilePath = outputPath.string();
    VideoWriter videoWriter(outputFilePath, VideoWriter::fourcc('X', 'V', 'I', 'D'), fps, Size(frame_width, frame_height));

    if (!videoWriter.isOpened())
    {
        std::cout << "Could not open the output video for write" << std::endl;
        return;
    }

    Mat frame = Mat::zeros(Size(frame_width, frame_height), CV_8UC3);
    for (int i = 0; i < 100; ++i)
    {
        frame.setTo(Scalar(i % 256, (i * 2) % 256, (i * 3) % 256));
        videoWriter.write(frame);
    }

    videoWriter.release();
    std::cout << "Video written successfully" << std::endl;
}

void MatDemo::enhance_test()
{
    Mat img = imread("../res/smoothed_image.jpg");

    // 去噪
    Mat denoise_img;
    bilateralFilter(img, denoise_img, 9, 75, 75);

    // 锐化
    Mat sharpen_img;
    Mat kernel = (Mat_<float>(3, 3) << 0, -1, 0,
                  -1, 5, -1,
                  0, -1, 0);
    filter2D(denoise_img, sharpen_img, -1, kernel);

    float hull_pts[] = {
        -90., -90., -90., -90., -90., -80., -80., -80., -80., -80., -80., -80., -80., -70., -70., -70., -70., -70., -70., -70., -70.,
        -70., -70., -60., -60., -60., -60., -60., -60., -60., -60., -60., -60., -60., -60., -50., -50., -50., -50., -50., -50., -50., -50.,
        -50., -50., -50., -50., -50., -50., -40., -40., -40., -40., -40., -40., -40., -40., -40., -40., -40., -40., -40., -40., -40., -30.,
        -30., -30., -30., -30., -30., -30., -30., -30., -30., -30., -30., -30., -30., -30., -30., -20., -20., -20., -20., -20., -20., -20.,
        -20., -20., -20., -20., -20., -20., -20., -20., -20., -10., -10., -10., -10., -10., -10., -10., -10., -10., -10., -10., -10., -10.,
        -10., -10., -10., -10., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 10., 10., 10., 10., 10., 10., 10.,
        10., 10., 10., 10., 10., 10., 10., 10., 10., 10., 10., 20., 20., 20., 20., 20., 20., 20., 20., 20., 20., 20., 20., 20., 20., 20.,
        20., 20., 20., 30., 30., 30., 30., 30., 30., 30., 30., 30., 30., 30., 30., 30., 30., 30., 30., 30., 30., 30., 40., 40., 40., 40.,
        40., 40., 40., 40., 40., 40., 40., 40., 40., 40., 40., 40., 40., 40., 40., 40., 50., 50., 50., 50., 50., 50., 50., 50., 50., 50.,
        50., 50., 50., 50., 50., 50., 50., 50., 50., 60., 60., 60., 60., 60., 60., 60., 60., 60., 60., 60., 60., 60., 60., 60., 60., 60.,
        60., 60., 60., 70., 70., 70., 70., 70., 70., 70., 70., 70., 70., 70., 70., 70., 70., 70., 70., 70., 70., 70., 70., 80., 80., 80.,
        80., 80., 80., 80., 80., 80., 80., 80., 80., 80., 80., 80., 80., 80., 80., 80., 90., 90., 90., 90., 90., 90., 90., 90., 90., 90.,
        90., 90., 90., 90., 90., 90., 90., 90., 90., 100., 100., 100., 100., 100., 100., 100., 100., 100., 100., 50., 60., 70., 80., 90.,
        20., 30., 40., 50., 60., 70., 80., 90., 0., 10., 20., 30., 40., 50., 60., 70., 80., 90., -20., -10., 0., 10., 20., 30., 40., 50.,
        60., 70., 80., 90., -30., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100., -40., -30., -20., -10., 0., 10., 20.,
        30., 40., 50., 60., 70., 80., 90., 100., -50., -40., -30., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100., -50.,
        -40., -30., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100., -60., -50., -40., -30., -20., -10., 0., 10., 20.,
        30., 40., 50., 60., 70., 80., 90., 100., -70., -60., -50., -40., -30., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., 80., 90.,
        100., -80., -70., -60., -50., -40., -30., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., 80., 90., -80., -70., -60., -50.,
        -40., -30., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., 80., 90., -90., -80., -70., -60., -50., -40., -30., -20., -10.,
        0., 10., 20., 30., 40., 50., 60., 70., 80., 90., -100., -90., -80., -70., -60., -50., -40., -30., -20., -10., 0., 10., 20., 30.,
        40., 50., 60., 70., 80., 90., -100., -90., -80., -70., -60., -50., -40., -30., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70.,
        80., -110., -100., -90., -80., -70., -60., -50., -40., -30., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., 80., -110., -100.,
        -90., -80., -70., -60., -50., -40., -30., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., 80., -110., -100., -90., -80., -70.,
        -60., -50., -40., -30., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., -110., -100., -90., -80., -70., -60., -50., -40., -30.,
        -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., -90., -80., -70., -60., -50., -40., -30., -20., -10., 0.};
    int sz[] = {2, 313, 1, 1};
    const int W_in = 224;
    const int H_in = 224;
    const Mat pts_in_hull(4, sz, CV_32F, hull_pts);
    cv::dnn::Net net = cv::dnn::readNetFromCaffe(
        "../models/colourfull/colorization_deploy_v2.prototxt",
        "../models/colourfull/dummy.caffemodel");
    Ptr<dnn::Layer> class8_ab = net.getLayer("class8_ab");
    class8_ab->blobs.push_back(pts_in_hull);
    Ptr<dnn::Layer> conv8_313_rh = net.getLayer("conv8_313_rh");
    conv8_313_rh->blobs.push_back(Mat(1, 313, CV_32F, Scalar(2.606)));

    // extract L channel and subtract mean
    Mat lab, L, input;
    sharpen_img.convertTo(sharpen_img, CV_32F, 1.0 / 255);
    cvtColor(sharpen_img, lab, COLOR_BGR2Lab);
    extractChannel(lab, L, 0);
    resize(L, input, Size(W_in, H_in));
    input -= 50;
    // run the L channel through the network
    Mat inputBlob = blobFromImage(input);
    net.setInput(inputBlob);
    Mat result = net.forward();
    // retrieve the calculated a,b channels from the network output
    Size siz(result.size[2], result.size[3]);
    Mat a = Mat(siz, CV_32F, result.ptr(0, 0));
    Mat b = Mat(siz, CV_32F, result.ptr(0, 1));
    resize(a, a, img.size());
    resize(b, b, img.size());
    // merge, and convert back to BGR
    Mat color, chn[] = {L, a, b};
    merge(chn, 3, lab);
    cvtColor(lab, color, COLOR_Lab2BGR);
    imshow("color", color);
    imshow("original", img);
    waitKey();
    destroyAllWindows();
}

void print_ms(const char *text=""){
    static long long last = 0;
    long long cur = getTickCount();
    if (last == 0) {
        last = cur;
    }
    long long ms = ((double)(cur-last)/getTickFrequency())*1000;
    if (*text!=0){
        cout<<text<<":"<<ms<<endl;
        last = 0;
    }
}

void MatDemo::new__test_1(cv::Mat &img)
{

    Mat new_img = Mat::zeros(1920, 1080, CV_8UC3);
    cout << "width:" << img.cols << ",height:" << img.rows << ",size:" << img.elemSize() << endl;
    int size = new_img.cols * new_img.rows * new_img.elemSize();
    //遍历方法1
    print_ms();
    for (int i = 0; i < size; i += 3)
    {
        new_img.data[i] = 0;
        new_img.data[i+1] = 255;
        new_img.data[i+2] = 255;
    }
    print_ms("result");

    //遍历方法2
    print_ms();
    for (int row=0;row<new_img.rows;row++){
        for (int col=0;col<new_img.cols;col++){
            (&new_img.data[row*new_img.step])[col*new_img.elemSize()] = 255;
            (&new_img.data[row*new_img.step])[col*new_img.elemSize()+1] = 255;
            (&new_img.data[row*new_img.step])[col*new_img.elemSize()+2] = 0;
        }
    }
    print_ms("result");

    imshow("new_img", new_img);
    waitKey(0);
    destroyAllWindows();
}


void MatDemo::threshold_test(Mat& img){
    Mat gray,bin;
    cvtColor(img, gray, COLOR_BGR2GRAY); //转成灰度图
    // threshold(gray, bin, 70, 255, THRESH_BINARY); //二进制阈值化
    threshold(gray, bin, 70, 255, THRESH_BINARY_INV);//反二进制阈值化

    // resize(bin, bin, Size(img.cols/2, img.rows/2));


    imshow("gray", gray);
    imshow("bin", bin);
    waitKey(0);
    destroyAllWindows();
}


void MatDemo::to_cartoon(Mat& img){
    // double t1 = getTickCount();

    // Mat gray;
    // cvtColor(img, gray, COLOR_BGR2GRAY); //灰度化
    // medianBlur(gray, gray, 5);//中值滤波降噪

    // //canny提取边缘
    // Canny(gray, gray, 120, 240);

    // //二值化后提取的边缘图像
    // Mat mask(img.cols, img.rows, CV_8U, Scalar(0));
    // threshold(gray, mask, 120, 255, THRESH_BINARY_INV);


    double t1 = getTickCount();
    Mat dststyle;
    stylization(img, dststyle, 100, 0.4);
    imshow("stylization", dststyle);

    waitKey(0);
    destroyAllWindows();

    t1= getTickCount() - t1;
    printf("time: %fms\n", t1*1000/getTickFrequency());
}
